package com.example.socketchat.client;

import com.example.socketchat.model.ChatMessage;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

/**
 * 模拟客户端类
 * 
 * 基于Netty实现的Socket客户端，用于模拟真实用户的连接和消息发送
 * 支持连接到Socket服务器、发送各类消息、接收服务器返回的消息
 * 内部集成了消息编解码器和自动化消息发送线程
 */
@Slf4j
public class MockClient implements Runnable {

    @Getter
    private final String clientId;      // 客户端唯一标识
    private final String serverHost;    // 服务器主机地址
    private final int serverPort;       // 服务器端口
    private final BlockingQueue<ChatMessage> receivedMessages = new LinkedBlockingQueue<>();  // 接收到的消息队列
    private final ObjectMapper objectMapper;  // JSON序列化工具
    
    private Channel channel;  // Netty通道
    private EventLoopGroup group;  // Netty事件循环组
    
    /**
     * 构造函数
     * 
     * @param clientId 客户端ID
     * @param serverHost 服务器主机地址
     * @param serverPort 服务器端口
     */
    public MockClient(String clientId, String serverHost, int serverPort) {
        this.clientId = clientId;
        this.serverHost = serverHost;
        this.serverPort = serverPort;
        
        // 初始化JSON序列化工具，并添加Java8时间模块支持
        this.objectMapper = new ObjectMapper();
        this.objectMapper.registerModule(new JavaTimeModule());
    }
    
    /**
     * 启动客户端并连接到服务器
     * 
     * @throws Exception 连接异常
     */
    public void start() throws Exception {
        // 创建NIO事件循环组
        group = new NioEventLoopGroup();
        
        try {
            // 创建客户端引导器
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)  // 使用NIO通道
                    .option(ChannelOption.SO_KEEPALIVE, true)  // 启用TCP保活机制
                    .option(ChannelOption.TCP_NODELAY, true)   // 禁用Nagle算法，提高实时性
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            
                            // 添加帧解码器，处理TCP粘包/拆包问题
                            pipeline.addLast(new LengthFieldBasedFrameDecoder(65536, 0, 4, 0, 4));
                            
                            // 添加帧编码器，在消息前面添加长度字段
                            pipeline.addLast(new LengthFieldPrepender(4));
                            
                            // 添加字符串编解码器
                            pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
                            pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
                            
                            // 添加自定义的消息处理器
                            pipeline.addLast(new MockClientHandler(receivedMessages, objectMapper));
                        }
                    });
            
            // 连接服务器并等待连接完成
            ChannelFuture future = bootstrap.connect(serverHost, serverPort).sync();
            channel = future.channel();
            
            log.info("客户端 {} 已连接到服务器 {}:{}", clientId, serverHost, serverPort);
            
            // 发送连接消息
            sendConnectMessage();
            
            // 开始模拟消息发送的线程
            new Thread(this).start();
            
        } catch (Exception e) {
            log.error("客户端 {} 连接服务器失败", clientId, e);
            group.shutdownGracefully();
            throw e;
        }
    }
    
    /**
     * 停止客户端，关闭连接和资源
     */
    public void stop() {
        if (channel != null) {
            channel.close();  // 关闭通道
        }
        
        if (group != null) {
            group.shutdownGracefully();  // 优雅关闭事件循环组
        }
        
        log.info("客户端 {} 已关闭", clientId);
    }
    
    /**
     * 发送连接消息，通知服务器客户端已连接
     */
    public void sendConnectMessage() {
        if (channel != null && channel.isActive()) {
            ChatMessage connectMessage = ChatMessage.createConnectMessage(clientId);
            sendMessage(connectMessage);
        }
    }
    
    /**
     * 发送点对点聊天消息
     * 
     * @param receiverId 接收者ID
     * @param content 消息内容
     */
    public void sendChatMessage(String receiverId, String content) {
        if (channel != null && channel.isActive()) {
            ChatMessage chatMessage = ChatMessage.createChatMessage(clientId, receiverId, content);
            sendMessage(chatMessage);
        }
    }
    
    /**
     * 发送广播消息，发送给所有人
     * 
     * @param content 消息内容
     */
    public void sendBroadcastMessage(String content) {
        if (channel != null && channel.isActive()) {
            ChatMessage broadcastMessage = ChatMessage.createBroadcastMessage(clientId, content);
            sendMessage(broadcastMessage);
        }
    }
    
    /**
     * 发送消息的内部方法
     * 
     * @param message 要发送的消息对象
     */
    private void sendMessage(ChatMessage message) {
        try {
            // 将消息对象转换为JSON字符串
            String jsonMessage = objectMapper.writeValueAsString(message);
            
            // 发送消息
            channel.writeAndFlush(jsonMessage).addListener((ChannelFutureListener) future -> {
                if (future.isSuccess()) {
                    log.debug("客户端 {} 发送消息成功: {}", clientId, message.getContent());
                } else {
                    log.error("客户端 {} 发送消息失败", clientId, future.cause());
                }
            });
        } catch (Exception e) {
            log.error("消息序列化失败", e);
        }
    }
    
    /**
     * 自动模拟发送消息的线程执行方法
     */
    @Override
    public void run() {
        try {
            while (channel != null && channel.isActive()) {
                // 随机等待一段时间
                TimeUnit.SECONDS.sleep(ThreadLocalRandom.current().nextInt(3, 10));
                
                // 50%概率发送广播消息，50%概率发送点对点消息
                if (ThreadLocalRandom.current().nextBoolean()) {
                    sendBroadcastMessage("来自客户端 " + clientId + " 的广播消息: " + System.currentTimeMillis());
                } else {
                    // 随机生成接收者ID（1-5）
                    int receiverId = ThreadLocalRandom.current().nextInt(1, 6);
                    String receiver = "client" + receiverId;
                    
                    // 不给自己发送消息
                    if (!receiver.equals(clientId)) {
                        sendChatMessage(receiver, "来自客户端 " + clientId + " 发送给 " + receiver + " 的消息: " + System.currentTimeMillis());
                    }
                }
            }
        } catch (InterruptedException e) {
            log.warn("客户端 {} 消息发送线程被中断", clientId);
            Thread.currentThread().interrupt();
        }
    }
    
    /**
     * 接收消息，从队列中获取接收到的消息
     * 
     * @return 接收到的消息，如果超时则返回null
     * @throws InterruptedException 中断异常
     */
    public ChatMessage receiveMessage() throws InterruptedException {
        return receivedMessages.poll(5, TimeUnit.SECONDS);
    }
    
    /**
     * 客户端消息处理器
     * 处理从服务器接收的消息
     */
    public static class MockClientHandler extends SimpleChannelInboundHandler<String> {
        
        private final BlockingQueue<ChatMessage> receivedMessages;
        private final ObjectMapper objectMapper;
        
        public MockClientHandler(BlockingQueue<ChatMessage> receivedMessages, ObjectMapper objectMapper) {
            this.receivedMessages = receivedMessages;
            this.objectMapper = objectMapper;
        }
        
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msgStr) throws Exception {
            // 将JSON字符串转换为ChatMessage对象
            ChatMessage message = objectMapper.readValue(msgStr, ChatMessage.class);
            
            // 将消息添加到队列
            receivedMessages.offer(message);
            
            log.debug("收到消息: {}", message.getContent());
        }
        
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            log.error("处理过程中发生异常", cause);
            ctx.close();
        }
    }
} 